package com.demo.watchconnectkitdemo;

import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.openharmony.watch.aidl.CancelFileTransferCallBack;
import com.openharmony.watch.aidl.Device;
import com.openharmony.watch.aidl.DeviceCallback;
import com.openharmony.watch.aidl.Message;
import com.openharmony.watch.aidl.Notification;
import com.openharmony.watch.aidl.NotifyCallback;
import com.openharmony.watch.aidl.Peer;
import com.openharmony.watch.aidl.PingCallback;
import com.openharmony.watch.aidl.Receiver;
import com.openharmony.watch.aidl.SendCallback;
import com.openharmony.watch.engine.WatchConnectEngine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import pub.devrel.easypermissions.EasyPermissions;

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
    private static final String TAG = "test-kitDemo";
    private RadioGroup devicesRadioGroup;
    private EditText messageEditText;
    private EditText logOutputTextView;
    private EditText peerPkgNameEditText;
    private EditText dstFilePathEditText;
    private List<Device> deviceList = new ArrayList<>();
    private Map<String, Device> deviceMap = new HashMap<>();
    private WatchConnectEngine engine = null;
    private Device selectedDevice = null;
    private Receiver receiver;
    private List<SendFileInfo> sendFileInfos = new ArrayList<>();
    private ActivityResultLauncher<Intent> serviceLauncher;
    private ActivityResultLauncher<String> fileLauncher;
    private static final int RC_PERMISSIONS = 100;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        Log.i(TAG, "MainActivity onCreate");
        initView();
        initPermissions();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (serviceLauncher != null) {
            serviceLauncher.unregister();
        }
        if (fileLauncher != null) {
            fileLauncher.unregister();
        }
    }

    public void initPermissions() {
        EasyPermissions.requestPermissions(this,
                "开始授权获取[媒体和文件]权限",
                RC_PERMISSIONS,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults, int deviceId) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults, deviceId);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        Log.d(TAG, "All permissions granted !!");
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        Log.d(TAG, "PermissionsDenied:" + perms);
        Toast.makeText(this, "有应用必须的权限被拒绝,会导致功能报错或退出,请前往[设置]->[应用和服务]->[权限管理]中授权", Toast.LENGTH_SHORT).show();
    }

    private void initView() {
        devicesRadioGroup = findViewById(R.id.device_radio_group);
        logOutputTextView = findViewById(R.id.log_output_text_view);
        messageEditText = findViewById(R.id.message_edit_text);
        peerPkgNameEditText = findViewById(R.id.peer_pkg_name_edit_text);
        dstFilePathEditText = findViewById(R.id.dst_file_path_edit_text);
        devicesRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
            Log.d(TAG, "onCheckedChanged:" + checkedId);
            selectedDevice = deviceList.get(checkedId);
        });
        serviceLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            Log.i(TAG, "serviceLauncher -> onActivityResult");
            if (result != null && result.getData() != null) {
                String packageName = result.getData().getStringExtra("packageName");
                engine = App.getWatchConnectEngine(packageName);
                if (engine != null) {
                    engine.getBondedDevices(new DeviceCallback.Stub() {
                        @Override
                        public void onGetResult(List<Device> devices) throws RemoteException {
                            Log.i(TAG, "getBondedDevices result, devices:" + devices);
                            deviceList.clear();
                            deviceList = devices;
                            updateDevices();
                            logOutputTextView.append("GetBoundDevices success." + System.lineSeparator());
                        }
                    });
                } else {
                    Log.e(TAG, "WatchConnectEngine is null");
                    logOutputTextView.append("WatchConnectEngine is null" + System.lineSeparator());
                    Toast.makeText(this, "WatchConnectEngine is null!!", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(this, "Must select a service!!", Toast.LENGTH_SHORT).show();
            }
        });

        fileLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
            Log.i(TAG, "fileLauncher -> onActivityResult:" + uri);
            String selectFilePath = SelectFileManager.getFilePath(MainActivity.this, uri);
            if (selectFilePath == null || selectFilePath.isEmpty()) {
                Log.e(TAG, "get file fail, maybe do not have permission");
                return;
            }
            Log.i(TAG, "selectFilePath:" + selectFilePath);
            sendFile(selectFilePath);
        });
    }

    public void onClickOfGetBoundDevices(View view) {
        Log.i(TAG, "onClickOfGetBoundDevices --> start");
        if (App.getWatchConnectEngineCount() == 1) {
            Log.i(TAG, "onClickOfGetBoundDevices --> only one service");
            engine = App.getWatchConnectEngine();
            if (engine != null) {
                engine.getBondedDevices(new DeviceCallback.Stub() {
                    @Override
                    public void onGetResult(List<Device> devices) {
                        Log.i(TAG, "getBondedDevices result, devices:" + devices);
                        deviceList.clear();
                        deviceList = devices;
                        updateDevices();
                        logOutputTextView.append("GetBoundDevices success." + System.lineSeparator());
                    }
                });
            }
        } else {
            Log.i(TAG, "onClickOfGetBoundDevices --> multi services");
            Intent intent = new Intent("com.openharmony.watch.SELECT_SERVICE_ACTIVITY");
            serviceLauncher.launch(intent);
        }
    }

    public void onClickOfGetAvailableKbytes(View view) {
        Log.i(TAG, "onClickOfGetDeviceFreeSize --> start");
        if (selectedDevice == null) {
            Toast.makeText(this, "select a device!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        long freeSize = engine.getAvailableKbytes(selectedDevice);
        String txt = "Device(" + selectedDevice.getName() + ")'s available byte(Kb): " + freeSize;
        Toast.makeText(this, txt, Toast.LENGTH_LONG).show();
        logOutputTextView.append(txt + System.lineSeparator());
    }

    private void updateDevices() {
        Log.i(TAG, "updateDevices deviceList size:" + deviceList.size());
        devicesRadioGroup.removeAllViews();
        deviceMap.clear();
        selectedDevice = null;
        for (int i = 0; i < deviceList.size(); i++) {
            if (deviceMap.containsKey(deviceList.get(i).getUuid())) {
                continue;
            }
            deviceList.add(deviceList.get(i));
            deviceMap.put(deviceList.get(i).getUuid(), deviceList.get(i));
            RadioButton deviceRadioButton = new RadioButton(this);
            setRaidButton(deviceRadioButton, deviceList.get(i).getName(), i);
            devicesRadioGroup.addView(deviceRadioButton);
        }
    }

    private void setRaidButton(final RadioButton radioButton, String text, int id) {
        radioButton.setChecked(false);
        radioButton.setId(id);
        radioButton.setText(text);
    }

    public void onClickOfPingApp(View view) {
        Log.i(TAG, "Button onClick -> onClickOfPingApp");
        if (selectedDevice == null) {
            Toast.makeText(this, "select a device!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        String srcPkgName = peerPkgNameEditText.getText().toString().trim();
        if (srcPkgName.isEmpty()) {
            Toast.makeText(this, "please input appPkgName or message for send!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("please input appPkgName or message for send!" + System.lineSeparator());
            return;
        }
        if (engine == null) {
            Log.i(TAG, "onClickOfPingApp --> WatchConnectEngine is null");
            Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
            return;
        }

        Peer peer = new Peer(selectedDevice, "fingerPrint", getPackageName(), srcPkgName);
        engine.ping(peer, new PingCallback.Stub() {
            @Override
            public void onPingResult(int errCode) {
                logOutputTextView.append("Ping Result code(" + errCode + ")" + System.lineSeparator());
            }
        });
    }

    public void onClickOfIsAppInstalled(View view) {
        Log.i(TAG, "Button onClick -> onClickOfIsAppInstalled");
        if (selectedDevice == null) {
            Toast.makeText(this, "select a device!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        if (peerPkgName.isEmpty()) {
            Toast.makeText(this, "please input appPkgName or message for send!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("please input appPkgName or message for send!" + System.lineSeparator());
            return;
        }
        if (engine == null) {
            Log.i(TAG, "onClickOfPingApp --> WatchConnectEngine is null");
            Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
            return;
        }
        Peer peer = new Peer(selectedDevice, "fingerPrint", getPackageName(), peerPkgName);
        boolean isInstalled =  engine.isAppInstalled(peer);
        String txt = "App(" + peerPkgName + ") on device:" + selectedDevice.getName() + " is " + (isInstalled ? "Installed" : "Uninstalled") +  ".";
        Toast.makeText(this, txt, Toast.LENGTH_LONG).show();
        logOutputTextView.append(txt + System.lineSeparator());
    }

    public void onClickOfGetAppVersion(View view) {
        Log.i(TAG, "Button onClick -> onClickOfGetAppVersion");
        if (selectedDevice == null) {
            Toast.makeText(this, "select a device!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        if (peerPkgName.isEmpty()) {
            Toast.makeText(this, "please input appPkgName or message for send!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("please input appPkgName or message for send!" + System.lineSeparator());
            return;
        }
        if (engine == null) {
            Log.i(TAG, "onClickOfPingApp --> WatchConnectEngine is null");
            Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
            return;
        }
        Peer peer = new Peer(selectedDevice, "fingerPrint", getPackageName(), peerPkgName);
        int version =  engine.getAppVersion(peer);
        String txt = "";
        if (version != -1) {
            txt = "App(" + peerPkgName + ") on device:" + selectedDevice.getName() + ", version:" + version;
        } else {
            txt = "App(" + peerPkgName + ") on device:" + selectedDevice.getName() + ", get version fail!!";
        }
        Toast.makeText(this, txt, Toast.LENGTH_LONG).show();
        logOutputTextView.append(txt + System.lineSeparator());
    }

    public void onClickOfSendMessage(View view) {
        Log.i(TAG, "Button onClick -> onClickOfSendMessage");
        if (selectedDevice == null) {
            Toast.makeText(this, "select a device!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        String sendMessageStr = messageEditText.getText().toString().trim();
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        if (sendMessageStr.isEmpty() || peerPkgName.isEmpty()) {
            Toast.makeText(this, "please input appPkgName or message for send!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("please input appPkgName or message for send!" + System.lineSeparator());
            return;
        }
        Peer peer = new Peer(selectedDevice, "", getPackageName(), peerPkgName);
        Message msg = new Message(0, sendMessageStr.getBytes(), "send message to server", "", "");
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (engine == null) {
                    Log.i(TAG, "onClickOfSendMessage --> WatchConnectEngine is null");
                    Toast.makeText(getApplicationContext(), "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
                    return;
                }
                engine.send(peer, msg, new SendCallback.Stub() {
                    @Override
                    public void onSendProgress(long count) throws RemoteException {
                        Log.i(TAG, "Send callback onSendProgress");
                        logOutputTextView.append("Send callback onSendProgress:" + count + System.lineSeparator());
                    }

                    @Override
                    public void onSendResult(int errCode) {
                        Log.i(TAG, "Send callback onSendResult code(" + errCode + ")");
                        logOutputTextView.append("Send callback onSendResult code(" + errCode + ")" + System.lineSeparator());
                    }
                });
            }
        }).start();
    }

    public void onClickOfSelectFileAndSend(View view) {
        Log.i(TAG, "Button onClick -> onClickOfSelectFileAndSend");
        initPermissions();
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        String dstFilePath = dstFilePathEditText.getText().toString().trim();
        if (selectedDevice == null || peerPkgName.isEmpty() || dstFilePath.isEmpty()) {
            Toast.makeText(this, "Device/AppPkgName/dstFilePath is null!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("Device/AppPkgName/dstFilePath is null!" + System.lineSeparator());
            return;
        }
        fileLauncher.launch("*/*");
    }

    private void sendFile(String srcfilePath) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String peerPkgName = peerPkgNameEditText.getText().toString().trim();
                String dstFilePath = dstFilePathEditText.getText().toString().trim();
                if (peerPkgName.isEmpty() || dstFilePath.isEmpty()) {
                    Toast.makeText(getApplicationContext(), "please input appPkgName/watch file path for send!", Toast.LENGTH_LONG).show();
                    logOutputTextView.append("please input appPkgName/watch file path for send!" + System.lineSeparator());
                    return;
                }
                Peer peer = new Peer(selectedDevice, "", getPackageName(), peerPkgName);
//                    ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(filePath), MODE_READ_WRITE);
                Message msg = new Message(1, "".getBytes() , "file description", srcfilePath, dstFilePath);
                if (engine == null) {
                    Log.i(TAG, "sendFile --> WatchConnectEngine is null");
                    Toast.makeText(getApplicationContext(), "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
                    return;
                }
                sendFileInfos.add(new SendFileInfo(selectedDevice.getName(), peerPkgName, srcfilePath));
                engine.send(peer, msg, new SendCallback.Stub() {
                    @Override
                    public void onSendProgress(long count) {
                        Log.i(TAG, "Send callback onSendProgress:" + count);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                logOutputTextView.append("Send callback onSendProgress:" + count + System.lineSeparator());
                            }
                        });
                    }

                    @Override
                    public void onSendResult(int errCode) {
                        Log.i(TAG, "Send callback onSendResult code(" + errCode + ")");
                        for (SendFileInfo info : sendFileInfos) {
                            if (info.pkgName.equals(peerPkgName) && info.deviceName.equals(selectedDevice.getName()) && info.filePath.equals(srcfilePath)) {
                                sendFileInfos.remove(info);
                                break;
                            }
                        }
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                logOutputTextView.append("Send callback onSendResult code(" + errCode + ")" + System.lineSeparator());
                            }
                        });
                    }
                });
            }
        }).start();
    }

    public void onClickOfCancelSendFile(View view) {
        Log.i(TAG, "Button onClick -> onClickOfCancelSendFile");
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        String dstFilePath = dstFilePathEditText.getText().toString().trim();
        if (selectedDevice == null || peerPkgName.isEmpty() || dstFilePath.isEmpty()) {
            Toast.makeText(this, "Device or AppPkgName is null!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        if (engine == null) {
            Log.i(TAG, "onClickOfPingApp --> WatchConnectEngine is null");
            Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
            return;
        }
        Peer peer = new Peer(selectedDevice, "", getPackageName(), peerPkgName);
        Message msg = null;
        for (SendFileInfo info : sendFileInfos) {
            if (info.pkgName.equals(peerPkgName) && info.deviceName.equals(selectedDevice.getName())) {
                msg = new Message(0, "".getBytes(), info.filePath, "", "");
                break;
            }
        }
        if (msg == null) {
            String txt = "Device(" + selectedDevice.getName() + ")-appPkgName(" + peerPkgName + ") do not have file transfer!!";
            Toast.makeText(this, txt, Toast.LENGTH_LONG).show();
            logOutputTextView.append(txt + System.lineSeparator());
            return;
        }
        engine.cancelFileTransfer(peer, msg, new CancelFileTransferCallBack.Stub() {
            @Override
            public void onCancelFileTransferResult(int errCode){
                for (SendFileInfo info : sendFileInfos) {
                    if (info.pkgName.equals(peerPkgName) && info.deviceName.equals(selectedDevice.getName())) {
                        sendFileInfos.remove(info);
                        break;
                    }
                }
            }
        });
    }

    public void onClickOfReceiveMessage(View view) {
        Log.i(TAG, "Button onClick -> onClickOfReceiveMessage");
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        if (selectedDevice == null || peerPkgName.isEmpty()) {
            Toast.makeText(this, "Device or AppPkgName is null!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        Peer peer = new Peer(selectedDevice, "", getPackageName(), peerPkgName);
        receiver = new Receiver.Stub() {
            @Override
            public void onReceiveMessage(Message message) {
                Log.i(TAG, "registerReceiver onReceiveMessage:" + message);
                logOutputTextView.append("registerReceiver onReceiveMessage:" + message + System.lineSeparator());
            }
        };
        if (engine == null) {
            Log.i(TAG, "onClickOfReceiveMessage --> WatchConnectEngine is null");
            Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
            return;
        }
        engine.registerReceiver(peer, receiver);
    }

    public void onClickOfCancelReceiveMessage(View view) {
        Log.i(TAG, "Button onClick -> onClickOfCancelReceiveMessage");
        if (receiver != null) {
            if (engine == null) {
                Log.i(TAG, "onClickOfCancelReceiveMessage --> WatchConnectEngine is null");
                Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
                return;
            }
            engine.unregisterReceiver(receiver);
        }
    }

    public void onClickOfStartNotify(View view) {
        Log.i(TAG, "Button onClick -> onClickOfStartNotify");
        String peerPkgName = peerPkgNameEditText.getText().toString().trim();
        if (selectedDevice == null || peerPkgName.isEmpty()) {
            Toast.makeText(this, "Device or AppPkgName is null!", Toast.LENGTH_LONG).show();
            logOutputTextView.append("select a device!" + System.lineSeparator());
            return;
        }
        Map<Integer, String> btns = new HashMap<>();
        btns.put(1, "11111");
        btns.put(2, "22222");
        btns.put(3, "33333");
        Notification notification = new Notification(btns, peerPkgName, 1, "message body" , "message title");

        if (engine == null) {
            Log.i(TAG, "onClickOfStartNotify --> WatchConnectEngine is null");
            Toast.makeText(this, "WatchConnectEngine is null", Toast.LENGTH_SHORT).show();
            return;
        }
        engine.notify(selectedDevice, notification, new NotifyCallback.Stub() {
            @Override
            public void onError(Notification notification, int errorCode, String errorMsg) {
                Log.e(TAG, "notify callback onError");
                Log.e(TAG, "notify callback onError, notification:" + notification + ", errorCode:" + errorCode + ", errorMsg:" + errorMsg);
                logOutputTextView.append("notify callback onError, notification:" + notification + ", errorCode:" + errorCode + ", errorMsg:" + errorMsg + System.lineSeparator());
            }

            @Override
            public void onResult(Notification notification, int feedback) {
                Log.i(TAG, "notify callback onResult");
                Log.i(TAG, "notify callback onResult, notification:" + notification + ", feedback:" + feedback);
                logOutputTextView.append("notify callback onResult, notification:" + notification + ", feedback:" + feedback + System.lineSeparator());
            }
        });
    }

    public void onClickOfClearOutputTextView(View view) {
        Log.i(TAG, "Button onClick -> onClickOfClearOutputTextView");
        logOutputTextView.setText("");
        logOutputTextView.scrollTo(0, 0);
    }

    private class SendFileInfo {
        public String deviceName;
        public String pkgName;
        public String filePath;

        public SendFileInfo(String deviceName, String pkgName, String filePath) {
            this.deviceName = deviceName;
            this.pkgName = pkgName;
            this.filePath = filePath;
        }
    }
}